package com.alibaba.citrus.ots.application.report.ability.repository.impl;

import com.alibaba.citrus.ots.application.report.ability.repository.BaseDataQueryRepository;
import com.alibaba.citrus.ots.application.report.ability.repository.BaseInventoryImportRepository;
import com.alibaba.citrus.ots.application.report.ability.utils.DomUtil;
import com.alibaba.citrus.ots.common.util.ThreadPageUtil;
import com.alibaba.cz.base.model.store.enums.CargoZoneTypeEnum;
import com.alibaba.cz.base.tool.StringUtil;
import com.alibaba.cz.base.tool.log.Log;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.lattice2.epoch.sdk.model.Result;
import com.epoch.app.bcorder.model.dto.OrganizationSDO;
import com.epoch.app.bcorder.model.dto.WarehouseInfo;
import com.epoch.app.crb2btradecenter.model.dto.CountSaleOrderForReportRequest;
import com.epoch.app.crb2btradecenter.model.dto.CountSaleOrderForReportResponse;
import com.epoch.app.crb2btradecenter.service.CrB2bTradeCenterService;
import com.epoch.app.crunifiedinventory.domain.allocateorderdetail.dto.GetAllocateUnshippedQuantityRequest;
import com.epoch.app.crunifiedinventory.domain.allocateorderdetail.service.AllocateOrderDetailService;
import com.epoch.app.crunifiedinventory.model.dto.AllocateUnshippedQuantitySDO;
import com.epoch.app.crunifiedinventory.model.dto.GetInventoryForAnalysisRequest;
import com.epoch.app.crunifiedinventory.model.dto.GetInventoryTotalForAnalysisRequest;
import com.epoch.app.crunifiedinventory.model.dto.GetInventoryTotalForAnalysisResponse;
import com.epoch.app.crunifiedinventory.model.dto.GetListWarehouseStockRequest;
import com.epoch.app.crunifiedinventory.model.dto.InventoryForAnalysisResp;
import com.epoch.app.crunifiedinventory.model.dto.WarehouseInfoDTO;
import com.epoch.app.crunifiedinventory.model.dto.WarehouseInventory;
import com.epoch.app.crunifiedinventory.service.CrUnifiedInventoryService;
import com.epoch.app.otsapplicationreportsdk.contants.OtsApplicationReportSdkConstants;
import com.epoch.app.otsapplicationreportsdk.model.dto.GoodsDO;
import com.epoch.app.otsapplicationreportsdk.model.enums.InventoryTypeEnum;
import com.epoch.app.otsapplicationreportsdk.warehouseinventorydetail.dto.BatchCreateRequest;
import com.epoch.app.otsapplicationreportsdk.warehouseinventorydetail.dto.BatchUpdateRequest;
import com.epoch.app.otsapplicationreportsdk.warehouseinventorydetail.dto.LoadListReadOnlyRequest;
import com.epoch.app.otsapplicationreportsdk.warehouseinventorydetail.dto.LoadListRequest;
import com.epoch.app.otsapplicationreportsdk.warehouseinventorydetail.model.WarehouseInventoryDetail;
import com.epoch.app.otsapplicationreportsdk.warehouseinventorydetail.service.WarehouseInventoryDetailService;
import com.epoch.app.otsapplicationreportsdk.warehouseinventorystats.dto.BatchUpdateInventoryQuantityRequest;
import com.epoch.app.otsapplicationreportsdk.warehouseinventorystats.dto.QueryByWarehouseAreaIdsReadOnlyRequest;
import com.epoch.app.otsapplicationreportsdk.warehouseinventorystats.dto.QueryByWarehouseAreaIdsRequest;
import com.epoch.app.otsapplicationreportsdk.warehouseinventorystats.model.WarehouseInventoryStats;
import com.epoch.app.otsapplicationreportsdk.warehouseinventorystats.service.WarehouseInventoryStatsService;
import com.google.common.collect.Lists;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Service;
import org.springframework.util.StopWatch;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Future;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.stream.Collectors;

@Service
public class WarehouseInventoryImportImpl implements BaseInventoryImportRepository {

    private final Log log = Log.getLogger(WarehouseInventoryImportImpl.class);

    private static final int PAGE_NO_1 = 1;

    private static final int PAGE_SIZE_400 = 400;

    private static final String SALE_ORG_CODE_EC = "1171";

    private static final String SALE_ORG_CODE_VIP = "6140";

    @Resource
    WarehouseInventoryDetailService warehouseInventoryDetailService;

    @Resource
    WarehouseInventoryStatsService warehouseInventoryStatsService;

    @Resource
    CrB2bTradeCenterService otsB2bTradeCenterService;

    @Resource
    CrUnifiedInventoryService otsUnifiedInventoryService;

    @Resource
    BaseDataQueryRepository baseDataQueryRepository;

    @Resource(name = "inventorySyncTaskExecutor")
    ThreadPoolTaskExecutor inventorySyncTaskExecutor;

    @Resource
    AllocateOrderDetailService allocateOrderDetailService;

    //拉取库存原子信息
    @Override
    public List<WarehouseInventory> getInventoryList() {
        //StopWatch stopwatch = new StopWatch();
        //List<Future<List<WarehouseInventory>>> futureList = Lists.newArrayList();
        //stopwatch.start("getTotal");
        //int totalCnt = getInventoryTotal(InventoryTypeEnum.WARE_HOUSE);
        //stopwatch.stop();
        //log.info("warehouseInventoryImportImpl_getInventoryTotal={},use_time={}ms", totalCnt,
        //    stopwatch.getTotalTimeSeconds());
        //int newsize = PAGE_SIZE_400;
        //int totalPage = totalCnt / newsize;
        //if (totalCnt % newsize > 0) {
        //    totalPage++;
        //}
        //for (int pageNo = PAGE_NO_1; pageNo <= totalPage; pageNo++) {
        //    int finalPageNo = pageNo;
        //    CompletableFuture<List<WarehouseInventory>> future = CompletableFuture.supplyAsync(() -> {
        //        Result<List<WarehouseInventory>> listResult = null;
        //        GetListWarehouseStockRequest request = new GetListWarehouseStockRequest();
        //        request.setType(String.valueOf(InventoryTypeEnum.WARE_HOUSE.getType()));
        //        request.setPage(finalPageNo);
        //        request.setSize(newsize);
        //        request.setAssembleInfo(true);
        //        try {
        //            listResult = otsUnifiedInventoryService.getListWarehouseStock(request);
        //            if (listResult == null || CollectionUtils.isEmpty(listResult.getResult())) {
        //                return Lists.newArrayList();
        //            }
        //            log.info(
        //                "warehouseInventoryImportImpl_getListWarehouseStock_request={},result_size={},"
        //                    + "haswarehousename={}",
        //                JSON.toJSONString(request), listResult.getResult().size(),
        //                JSON.toJSONString(listResult.getResult().get(0)));
        //        } catch (Exception e) {
        //            log.error("warehouseInventoryImportImpl_pull_getListWarehouseStock_error={}", e.getMessage());
        //        }
        //        List<WarehouseInventory> inventorySubList = listResult.getResult();
        //        return inventorySubList;
        //    }, inventorySyncTaskExecutor);
        //    futureList.add(future);
        //}
        //List<WarehouseInventory> result = new ArrayList<>();
        //try {
        //    int futureTimes = 1;
        //    for (Future<List<WarehouseInventory>> future : futureList) {
        //        List<WarehouseInventory> inventorySubList = future.get();
        //        result.addAll(inventorySubList);
        //        futureTimes++;
        //    }
        //} catch (Exception e) {
        //    log.error("warehouseInventoryImportImpl_pull_getListWarehouseStock_wait_occur_error={}", e.getMessage());
        //}
        //List<String> ecWarehouseIds = getEcWarehouseIdsByOrgCode(SALE_ORG_CODE_EC);
        //ecWarehouseIds.addAll(getEcWarehouseIdsByOrgCode(SALE_ORG_CODE_VIP));
        //List<WarehouseInventory> filterResult = result.stream().filter(
        //    s -> !isEcWarehouse(s.getWarehouseAreaId(), ecWarehouseIds)).collect(Collectors.toList());
        //log.info("getInventoryList_stopWatch_prettyPrint={}", stopwatch.prettyPrint());
        return new ArrayList<>();
    }

    @Override
    public List<WarehouseInventory> getInventoryList(List<String> warehouseAreaIds, Integer size) {
        StopWatch stopwatch = new StopWatch();
        List<Future<List<WarehouseInventory>>> futureList = Lists.newArrayList();
        stopwatch.start("getTotal");
        GetInventoryTotalForAnalysisRequest request = new GetInventoryTotalForAnalysisRequest();
        request.setType(InventoryTypeEnum.WARE_HOUSE.getType());
        request.setWarehouseAreaIdList(warehouseAreaIds);
        GetInventoryTotalForAnalysisResponse totalResult = otsUnifiedInventoryService.getInventoryTotalForAnalysis(request);
        int totalCnt = totalResult.getResult();
        stopwatch.stop();
        log.info("warehouseInventoryImportImpl_getInventoryTotal={},use_time={}ms", totalCnt,stopwatch.getTotalTimeSeconds());
        int newsize = (size != null && !size.equals(0)) ? size : PAGE_SIZE_400;
        int totalPage = ThreadPageUtil.calculateTotalPage(totalCnt, newsize);
        log.info("warehouseInventoryImportImpl_getInventoryTotal={},use_time={}ms,totalpage:{}", totalCnt,stopwatch.getTotalTimeSeconds(),totalPage);
        for (int pageNo = PAGE_NO_1; pageNo <= totalPage; pageNo++) {
            int finalPageNo = pageNo;
            CompletableFuture<List<WarehouseInventory>> future = CompletableFuture.supplyAsync(() -> {
                List<WarehouseInventory> listResult = null;
                GetInventoryForAnalysisRequest futurerequest = new GetInventoryForAnalysisRequest();
                futurerequest.setType(InventoryTypeEnum.WARE_HOUSE.getType());
                futurerequest.setPage(finalPageNo);
                futurerequest.setSize(newsize);
                futurerequest.setWarehouseAreaIdList(warehouseAreaIds);
                try {
                    Result<List<InventoryForAnalysisResp>> resp = otsUnifiedInventoryService.getInventoryForAnalysis(futurerequest);
                    if (resp.isSuccess() && CollectionUtils.isEmpty(resp.getResult())) {
                        return Lists.newArrayList();
                    }
                    listResult = formatQuantity(resp.getResult());
                } catch (Exception e) {
                    log.error("warehouseInventoryImportImpl_pull_getListWarehouseStock_error={}", e.getMessage());
                }
                log.error("warehouseInventoryImportImpl_pull_getListWarehouseStock_info={},resultSize={}",JSON.toJSON(futurerequest), listResult.size());
                return listResult;
            }, inventorySyncTaskExecutor);
            futureList.add(future);
        }
        List<WarehouseInventory> result = new ArrayList<>();
        try {
            stopwatch.start("listwarehouseinventory");
            int futureTimes = 1;
            for (Future<List<WarehouseInventory>> future : futureList) {
                List<WarehouseInventory> inventorySubList = future.get();
                result.addAll(inventorySubList);
                futureTimes++;
            }
            stopwatch.stop();
            log.info("warehouseInventoryImportImpl_use_time={}s,foreachtimes={}s,inventorySubList_size={}",stopwatch.getTotalTimeSeconds(), futureTimes, result.size());
        } catch (Exception e) {
            log.error("warehouseInventoryImportImpl_pull_getListWarehouseStock_wait_occur_error={}", e.getMessage());
        }
        return result;
    }

    /**
     * 查出来的数量没有格式化
     *
     * @param beforFormatList
     * @return
     */
    private List<WarehouseInventory> formatQuantity(List<InventoryForAnalysisResp> beforFormatList) {
        List<WarehouseInventory> result = new ArrayList<>();
        for (InventoryForAnalysisResp x : beforFormatList) {
            WarehouseInventory y = new WarehouseInventory();
            y.setWarehouseAreaId(x.getWarehouseAreaId());
            y.setDom(x.getDom());
            y.setTotalQuantity(x.getTotalQuantity());
            y.setInspectQuantity(x.getInspectQuantity());
            y.setWithholdingQuantity(x.getWithholdingQuantity());
            y.setOnwayQuantity(x.getOnwayQuantity());
            y.setFrozenQuantity(x.getFrozenQuantity());
            y.setAvailableQuantity(x.getAvailableQuantity());
            y.setGoodsId(x.getGoodsId());
            y.setUnitConvert(x.getUnitConvert());
            y.setUnitConvertedQuantity(x.getUnitConvertedQuantity());
            result.add(y);
        }
        return result;

    }

    @Override
    public void doImportData(List<WarehouseInventory> importInventoryList, List<String> importWarehouseAreaIds, Map<String, GoodsDO> goodsDOMap, boolean firstSyncOfToday) {
        List<WarehouseInventoryStats> statsList = new ArrayList<>();
        //逻辑仓
        Set<String> warehouseAreaIds = importInventoryList.stream().map(WarehouseInventory::getWarehouseAreaId).collect(Collectors.toSet());
        Map<String, WarehouseInfoDTO> warehouseAreaMap = baseDataQueryRepository.getLogicZoneInfo(Lists.newArrayList(warehouseAreaIds));
        //仓库档案 父级
        List<String> warehouseIds = new ArrayList<>();
        //逻辑仓的销售组织
        List<String> saleOrgIds = new ArrayList<>();
        warehouseAreaMap.entrySet().stream().forEach(new Consumer<Entry<String, WarehouseInfoDTO>>() {
            @Override
            public void accept(Entry<String, WarehouseInfoDTO> x) {
                warehouseIds.add(x.getValue().getWarehouseId());
                saleOrgIds.add(x.getValue().getSaleOrgId());
            }
        });
        Map<String, OrganizationSDO> orgMap = baseDataQueryRepository.batchQueryOrganization(saleOrgIds);
        Map<String, WarehouseInfo> warehouseMap = baseDataQueryRepository.getWarehouseInfo(warehouseIds);
        Map<String, List<WarehouseInventory>> mapOrg = importInventoryList.stream().collect(Collectors.groupingBy(x -> x.getWarehouseAreaId()));
        log.info("warehouseInventoryImportImpl_warehouseAreaId_total={}", mapOrg.size());
        int total = 0;
        for (Map.Entry<String, List<WarehouseInventory>> entryOrg : mapOrg.entrySet()) {
            String warehouseAreaId = entryOrg.getKey();
            WarehouseInfoDTO logicZoneDTO = warehouseAreaMap.get(warehouseAreaId);
            if (logicZoneDTO == null) {
                log.info("warehouseInventoryImportImpl_logicZoneDTO_is_null,warehouseAreaId={}", warehouseAreaId);
                continue;
            }
            String code = logicZoneDTO.getWarehouseCode() != null ? logicZoneDTO.getWarehouseCode() : "";
            Integer type = logicZoneDTO.getType() != null ? logicZoneDTO.getType() : -1;
            //过滤掉工厂仓
            if (CargoZoneTypeEnum.FACTORY_CARGOZONE.getType().equals(type)) {
                log.info("warehouseInventoryImportImpl_doImportData_warehouseAreaId={},zoneType={},discard!!!",warehouseAreaId, type);
                continue;
            }
            //过滤共享仓的库存地点是DEFAULT的
            if (CargoZoneTypeEnum.SHARE_CARGOZONE.getType().equals(type) && "DEFAULT".equals(code)) {
                log.info("warehouseInventoryImportImpl_doImportData_warehouseAreaId={},warehouseCode=DEFAULT,discard!!!",warehouseAreaId);
                continue;
            }
            List<WarehouseInventory> orgInventoryList = entryOrg.getValue();
            Map<String, List<WarehouseInventory>> mapItem = orgInventoryList.stream().collect(Collectors.groupingBy(x -> x.getGoodsId()));

            List<WarehouseInventoryDetail> detailList = new ArrayList<>();
            for (Map.Entry<String, List<WarehouseInventory>> entryItem : mapItem.entrySet()) {
                WarehouseInventoryDetail detail = buildInventoryDetail(entryItem, goodsDOMap);
                if (detail != null) {
                    detailList.add(detail);
                }
            }
            total += detailList.size();
            log.info("warehouseInventoryImportImpl_doImportData_warehouseAreaId={},detailList_size={}", warehouseAreaId,detailList.size());

            if (CollectionUtils.isNotEmpty(detailList)) {
                //重算直接可用库存和占用量
                try {
                    if (CargoZoneTypeEnum.SHARE_CARGOZONE.getType().equals(type)) {
                        this.handleInventoryOccupy(warehouseAreaId, detailList);
                    }
                } catch (Exception e) {
                    log.error("handleInventoryOccupy_error={}", e);
                }
                //增补主数据
                String warehouseId = orgInventoryList.get(0).getWarehouseId();
                String warehouseName = warehouseMap.get(warehouseId) != null ? warehouseMap.get(warehouseId).getName(): "-";
                String logicZoneName = StringUtils.isNotBlank(logicZoneDTO.getName()) ? logicZoneDTO.getName() : "";
                String finalName = warehouseName + "/" + logicZoneName;
                String saleOrgId = StringUtil.isNotEmpty(logicZoneDTO.getSaleOrgId()) ? logicZoneDTO.getSaleOrgId(): "-1";
                String bizUnitId = StringUtil.isNotEmpty(saleOrgId) && orgMap.get(saleOrgId) != null ? orgMap.get(saleOrgId).getParentId() : "-1";
                if ("-1".equals(saleOrgId) || "-1".equals(bizUnitId)) {
                    log.info("warehouseInventoryImportImpl_warehouseAreaId_is_null_or_bizUnitId_is_null,DTO={}",logicZoneDTO.toString());
                }
                detailList.stream().forEach(x -> {
                    x.setWarehouseAreaName(finalName);
                    x.setZoneType(String.valueOf(type));
                    x.setSaleOrgId(saleOrgId);
                    x.setBizUnitId(bizUnitId);
                });
                saveInventoryDetail(warehouseAreaId, detailList, firstSyncOfToday);
                WarehouseInventoryStats stats = collectInventoryStats(detailList);
                if(stats!=null) {
                    statsList.add(stats);
                }else{
                    log.info("warehouseInventoryImportImpl_statsList add error={}", JSON.toJSON(detailList));
                }
            }
        }
        log.info("warehouseInventoryImportImpl_doImportData_total_detail_size={}", total);
        if (CollectionUtils.isNotEmpty(statsList)) {
            saveInventoryStats(statsList, firstSyncOfToday);
        }
        log.info("warehouseInventoryImportImpl_savestats,statsList_size={}", statsList.size());

        //报表数据没做对比的，报表库存全部置为0
        if(!firstSyncOfToday){
            String statisticalDate = DomUtil.getToday();
            List<String> notCompareWarehouseAreaIds = importWarehouseAreaIds.stream().filter(x->!warehouseAreaIds.contains(x)).collect(Collectors.toList());
            if(CollectionUtils.isEmpty(notCompareWarehouseAreaIds)){
                return;
            }
            QueryByWarehouseAreaIdsRequest queryRequest = new QueryByWarehouseAreaIdsRequest();
            queryRequest.setStatisticalDate(statisticalDate);
            queryRequest.setWarehouseAreaIds(notCompareWarehouseAreaIds);
            Result<List<WarehouseInventoryStats>> reportQueryResult = warehouseInventoryStatsService.queryByWarehouseAreaIds(queryRequest);
            if(Objects.nonNull(reportQueryResult) && CollectionUtils.isNotEmpty(reportQueryResult.getResult())){
                List<String> updateWarehouseAreaIds = reportQueryResult.getResult().stream()
                        .filter(x->!isZeroInventoryQuantity(null,x))
                        .map(x->x.getWarehouseAreaId()).collect(Collectors.toList());
                if(CollectionUtils.isNotEmpty(updateWarehouseAreaIds)){
                    log.info("warehouseInventoryImportImpl_notCompareWarehouseIds={}", JSONObject.toJSONString(updateWarehouseAreaIds));

                    WarehouseInventoryDetail detailUpdate = new WarehouseInventoryDetail();
                    WarehouseInventoryStats statsUpdate = new WarehouseInventoryStats();
                    initZeroInventoryQuantity(detailUpdate,statsUpdate);
                    BatchUpdateRequest batchUpdateDetailRequest = new BatchUpdateRequest();
                    BeanUtils.copyProperties(detailUpdate,batchUpdateDetailRequest);
                    batchUpdateDetailRequest.setStatisticalDate(statisticalDate);
                    batchUpdateDetailRequest.setWarehouseAreaIds(updateWarehouseAreaIds);
                    warehouseInventoryDetailService.batchUpdate(batchUpdateDetailRequest);
                    BatchUpdateInventoryQuantityRequest batchUpdateStatsRequest = new BatchUpdateInventoryQuantityRequest();
                    BeanUtils.copyProperties(statsUpdate,batchUpdateStatsRequest);
                    batchUpdateStatsRequest.setStatisticalDate(statisticalDate);
                    batchUpdateStatsRequest.setWarehouseAreaIds(updateWarehouseAreaIds);
                    warehouseInventoryStatsService.batchUpdateInventoryQuantity(batchUpdateStatsRequest);
                }
            }
        }
    }

    private void saveInventoryDetail(String warehouseAreaId, List<WarehouseInventoryDetail> todoList, boolean firstSyncOfToday) {
        //当体首次同步则直接插入
        if (firstSyncOfToday) {
            BatchCreateRequest batchCreateRequest = new BatchCreateRequest();
            batchCreateRequest.setReportList(todoList);
            Object response = warehouseInventoryDetailService.batchCreate(batchCreateRequest);
        } else {
            //根据目前纪元单次最多只能查出1000条的数据限制，若未来单仓出现全链路明细超过1000条，会存在重复数据风险
            LoadListRequest request = new LoadListRequest();
            request.setStart(0);
            request.setLimit(2000);
            request.setWarehouseAreaId(warehouseAreaId);
            request.setStatisticalDate(DomUtil.getToday());
            Result<List<WarehouseInventoryDetail>> response;
            if ("1".equals(OtsApplicationReportSdkConstants.QUERY_READONLY_DATABASE())) {
                LoadListReadOnlyRequest readOnlyRequest = new LoadListReadOnlyRequest();
                BeanUtils.copyProperties(request, readOnlyRequest);
                response = warehouseInventoryDetailService.loadListReadOnly(readOnlyRequest);
            } else {
                response = warehouseInventoryDetailService.loadList(request);
            }
            List<WarehouseInventoryDetail> existedList = response.getResult();
            for (WarehouseInventoryDetail todo : todoList) {
                //已存在的数据则更新
                WarehouseInventoryDetail existed = existedOfList(todo, existedList);
                if (existed != null) {
                    if (!needUpdate(todo, existed)) {
                        existed.setUpdater("1");
                        log.info("WarehouseInventoryDetail_exist_and_nochanged,id={}", existed.getGoodsId());
                        continue;
                    }
                    BeanUtils.copyProperties(todo, existed, "id");
                    existed.setGmt_modified(new Date());
                    Object result = warehouseInventoryDetailService.update(existed);
                    existed.setUpdater("1");
                } else {
                    Object result = warehouseInventoryDetailService.create(todo);
                }
            }

            //报表数据没做对比的，报表库存全部置为0
            List<WarehouseInventoryDetail> notCompareDetailList = existedList.stream()
                    .filter(x->!"1".equals(x.getUpdater()))
                    .filter(x->!isZeroInventoryQuantity(x,null))//过滤库存已经是0的不做数据库变更
                    .collect(Collectors.toList());
            if(CollectionUtils.isNotEmpty(notCompareDetailList)){
                List<Long> ids = notCompareDetailList.stream().map(x->x.getId()).collect(Collectors.toList());
                log.info("WarehouseInventoryDetail_exist_and_notCompareDetail,warehouseAreaId={},list={}", warehouseAreaId, JSONObject.toJSONString(ids));
                for (WarehouseInventoryDetail detail : notCompareDetailList) {
                    initZeroInventoryQuantity(detail,null);
                    Object updateResponse = warehouseInventoryDetailService.update(detail);
                }
            }
        }
    }

    //判断是否当前待处理记录是否已存在
    private WarehouseInventoryDetail existedOfList(WarehouseInventoryDetail todo,
        List<WarehouseInventoryDetail> existedList) {
        if (CollectionUtils.isEmpty(existedList)) {
            return null;
        }
        for (WarehouseInventoryDetail existed : existedList) {
            if (existed.getGoodsId().equals(todo.getGoodsId())) {
                return existed;
            }
        }
        return null;
    }

    //判断是否当前待处理记录是否已存在
    private WarehouseInventoryStats existedOfList(WarehouseInventoryStats todo,
        List<WarehouseInventoryStats> existedList) {
        if (CollectionUtils.isEmpty(existedList)) {
            return null;
        }
        for (WarehouseInventoryStats existed : existedList) {
            if (existed.getWarehouseAreaId().equals(todo.getWarehouseAreaId())) {
                return existed;
            }
        }
        return null;
    }

    //判断第一天首次同步
    @Override
    public boolean firstSyncOfToday() {
        com.epoch.app.otsapplicationreportsdk.warehouseinventorystats.dto.LoadListRequest request
            = new com.epoch.app.otsapplicationreportsdk.warehouseinventorystats.dto.LoadListRequest();
        request.setStatisticalDate(DomUtil.getToday());
        return CollectionUtils.isEmpty(warehouseInventoryStatsService.loadList(request).getResult());
    }

    //判断是否需要修改
    private boolean needUpdate(WarehouseInventoryDetail todo, WarehouseInventoryDetail existed) {
        return !todo.getTotalQuantity().equals(existed.getTotalQuantity()) ||
            !todo.getAvailableQuantity().equals(existed.getAvailableQuantity()) ||
            !todo.getWithholdingQuantity().equals(existed.getWithholdingQuantity()) ||
            !todo.getInspectQuantity().equals(existed.getInspectQuantity()) ||
            !todo.getOnwayQuantity().equals(existed.getOnwayQuantity()) ||
            !todo.getUsableQuantity().equals(existed.getUsableQuantity());
    }

    //判断是否需要修改
    private boolean needModify(WarehouseInventoryStats todo, WarehouseInventoryStats existed) {
        return !todo.getTotalQuantity().equals(existed.getTotalQuantity()) ||
            !todo.getAvailableQuantity().equals(existed.getAvailableQuantity()) ||
            !todo.getWithholdingQuantity().equals(existed.getWithholdingQuantity()) ||
            !todo.getInspectQuantity().equals(existed.getInspectQuantity()) ||
            !todo.getOnwayQuantity().equals(existed.getOnwayQuantity());
    }

    private void saveInventoryStats(List<WarehouseInventoryStats> todoList, boolean firstSyncOfToday) {
        //当体首次同步则直接插入
        if (firstSyncOfToday) {
            com.epoch.app.otsapplicationreportsdk.warehouseinventorystats.dto.BatchCreateRequest batchCreateRequest
                = new com.epoch.app.otsapplicationreportsdk.warehouseinventorystats.dto.BatchCreateRequest();
            batchCreateRequest.setReportList(todoList);
            log.info("firstinsertWarehouseInventoryStats_batchCreateRequest_listsize={}",batchCreateRequest.getReportList().size());
            Object response = warehouseInventoryStatsService.batchCreate(batchCreateRequest);
        } else {
            //分析条数超过2000会有问题,改为1条条查对比
            for (WarehouseInventoryStats todo : todoList) {
                QueryByWarehouseAreaIdsRequest request = new QueryByWarehouseAreaIdsRequest();
                request.setWarehouseAreaIds(Arrays.asList(todo.getWarehouseAreaId()));
                request.setStatisticalDate(DomUtil.getToday());
                Result<List<WarehouseInventoryStats>> response;
                if ("1".equals(OtsApplicationReportSdkConstants.QUERY_READONLY_DATABASE())) {
                    QueryByWarehouseAreaIdsReadOnlyRequest readOnlyRequest = new QueryByWarehouseAreaIdsReadOnlyRequest();
                    BeanUtils.copyProperties(request, readOnlyRequest);
                    response = warehouseInventoryStatsService.queryByWarehouseAreaIdsReadOnly(readOnlyRequest);
                } else {
                    response = warehouseInventoryStatsService.queryByWarehouseAreaIds(request);
                }
                List<WarehouseInventoryStats> existedList = new ArrayList<>();
                if(Objects.nonNull(response) && CollectionUtils.isNotEmpty(response.getResult())){
                    existedList = response.getResult();
                }
                WarehouseInventoryStats existed = existedOfList(todo, existedList);
                //已存在的数据则更新
                if (existed != null) {
                    if (!needModify(todo, existed)) {
                        log.info("WarehouseInventoryStats_exist_and_nochanged,id={}", existed.getId());
                        continue;
                    }
                    BeanUtils.copyProperties(todo, existed, "id");
                    existed.setGmt_modified(new Date());
                    Object updateResponse = warehouseInventoryStatsService.update(existed);
                } else {
                    Object createResponse = warehouseInventoryStatsService.create(todo);
                }
            }
        }
    }

    private WarehouseInventoryDetail buildInventoryDetail(Map.Entry<String, List<WarehouseInventory>> entry,
        Map<String, GoodsDO> goodsDOMap) {
        GoodsDO goodsDO = goodsDOMap.get(entry.getKey());
        if (goodsDO == null) {
            log.info("goods_exist_warehouseInventory_but_scItem_id_error={}", entry.getKey());
            return null;
        }
        List<WarehouseInventory> itemInventoryList = entry.getValue();
        return collectInventoryDetail(goodsDO, itemInventoryList);
    }

    //保存明细表
    private WarehouseInventoryDetail collectInventoryDetail(GoodsDO goodsDO, List<WarehouseInventory> inventoryList) {
        log.info("collectInventoryDetail_warehouseAreaId={},goods_id={},total={}",inventoryList.get(0).getWarehouseAreaId(), goodsDO.getGoodsId(), inventoryList.size());
        WarehouseInventoryDetail detail = new WarehouseInventoryDetail();
        detail.setWarehouseAreaId(inventoryList.get(0).getWarehouseAreaId());
        //detail.setWarehouseAreaName(inventoryList.get(0).getWarehouseName());
        detail.setGoodsId(goodsDO.getGoodsId());
        detail.setOuterItemId(goodsDO.getOuterItemId());
        detail.setGoodsName(goodsDO.getGoodsName());
        detail.setCategoryId(goodsDO.getCategoryId());
        detail.setCategoryName(goodsDO.getCategoryName());
        detail.setRootCategoryId(goodsDO.getRootCategoryId());
        detail.setRootCategoryName(goodsDO.getRootCategoryName());
        detail.setStatisticalDate(DomUtil.getToday());
        Long totalQuantity = inventoryList.stream().mapToLong(WarehouseInventory::getTotalQuantity).sum();
        detail.setTotalQuantity(totalQuantity);
        Long frozenQuantity = inventoryList.stream().mapToLong(WarehouseInventory::getFrozenQuantity).sum();
        detail.setFrozenQuantity(frozenQuantity);
        Long inspectQuantity = inventoryList.stream().mapToLong(WarehouseInventory::getInspectQuantity).sum();
        detail.setInspectQuantity(inspectQuantity);
        //可用库存=总库存-冻结库存-质检库存
        //2022.12.20 可用的计算公式应该是 可用 = 总库存 - 冻结 - 质检 - 占用吧
        Long availableQuantity = inventoryList.stream().mapToLong(WarehouseInventory::getAvailableQuantity).sum();
        detail.setAvailableQuantity(availableQuantity);
//        Long availableQuantity = totalQuantity - frozenQuantity - inspectQuantity;
//        detail.setAvailableQuantity(availableQuantity);
        detail.setWithholdingQuantity(inventoryList.stream().mapToLong(WarehouseInventory::getWithholdingQuantity).sum());
        detail.setOnwayQuantity(inventoryList.stream().mapToLong(WarehouseInventory::getOnwayQuantity).sum());
        //生产月份
        Long exceedThreeMonthsQuantity = 0L;
        Long earlyTwoMonthsQuantity = 0L;
        Long midTwoMonthsQuantity = 0L;
        Long lateTwoMonthsQuantity = 0L;
        Long earlyOneMonthQuantity = 0L;
        Long midOneMonthQuantity = 0L;
        Long lateOneMonthQuantity = 0L;
        Long earlyThisMonthQuantity = 0L;
        Long midThisMonthQuantity = 0L;
        Long lateThisMonthQuantity = 0L;
        for (WarehouseInventory inventory : inventoryList) {
            Long realQuantity = inventory.getTotalQuantity() - inventory.getFrozenQuantity() - inventory
                .getInspectQuantity();
            if (DomUtil.threeMonthsAgo(inventory.getDom())) {
                exceedThreeMonthsQuantity += realQuantity;
            }
            if (DomUtil.twoMonthsAgoEarly(inventory.getDom())) {
                earlyTwoMonthsQuantity += realQuantity;
            }
            if (DomUtil.twoMonthsAgoMid(inventory.getDom())) {
                midTwoMonthsQuantity += realQuantity;
            }
            if (DomUtil.twoMonthsAgoLate(inventory.getDom())) {
                lateTwoMonthsQuantity += realQuantity;
            }
            if (DomUtil.oneMonthAgoEarly(inventory.getDom())) {
                earlyOneMonthQuantity += realQuantity;
            }
            if (DomUtil.oneMonthAgoMid(inventory.getDom())) {
                midOneMonthQuantity += realQuantity;
            }
            if (DomUtil.oneMonthAgoLate(inventory.getDom())) {
                lateOneMonthQuantity += realQuantity;
            }
            if (DomUtil.thisMonthEarly(inventory.getDom())) {
                earlyThisMonthQuantity += realQuantity;
            }
            if (DomUtil.thisMonthMid(inventory.getDom())) {
                midThisMonthQuantity += realQuantity;
            }
            if (DomUtil.thisMonthLate(inventory.getDom())) {
                lateThisMonthQuantity += realQuantity;
            }
        }
        detail.setExceedThreeMonthsQuantity(exceedThreeMonthsQuantity);
        detail.setEarlyTwoMonthsQuantity(earlyTwoMonthsQuantity);
        detail.setMidTwoMonthsQuantity(midTwoMonthsQuantity);
        detail.setLateTwoMonthsQuantity(lateTwoMonthsQuantity);
        detail.setEarlyOneMonthQuantity(earlyOneMonthQuantity);
        detail.setMidOneMonthQuantity(midOneMonthQuantity);
        detail.setLateOneMonthQuantity(lateOneMonthQuantity);
        detail.setEarlyThisMonthQuantity(earlyThisMonthQuantity);
        detail.setMidThisMonthQuantity(midThisMonthQuantity);
        detail.setLateThisMonthQuantity(lateThisMonthQuantity);
        //大日期
        Long fourthExpiredQuantity = 0L;
        Long thirdExpiredQuantity = 0L;
        Long approachExpiredQuantity = 0L;
        Long normalQuantity = 0L;
        Long fourthExpiredTotal = 0L;
        Long thirdExpiredTotal = 0L;
        Long approachExpiredTotal = 0L;
        Long normalTotal = 0L;
        Integer guaranteePeriod = goodsDO.getGuaranteePeriod();
        if (!guaranteePeriod.equals(0)) {
            detail.setSpecTag("0");
            for (WarehouseInventory inventory : inventoryList) {
                Long realQuantity = inventory.getTotalQuantity() - inventory.getFrozenQuantity() - inventory
                    .getInspectQuantity();
                if (DomUtil.exceedFourthExpired(inventory.getDom(), guaranteePeriod)) {
                    fourthExpiredQuantity += realQuantity;
                }
                if (DomUtil.exceedThirdExpired(inventory.getDom(), guaranteePeriod)) {
                    thirdExpiredQuantity += realQuantity;
                }
                if (DomUtil.approachExpired(inventory.getDom(), guaranteePeriod)) {
                    approachExpiredQuantity += realQuantity;
                }
                if (DomUtil.lessFourthExpired(inventory.getDom(), guaranteePeriod)) {
                    normalQuantity += realQuantity;
                }
            }
            fourthExpiredTotal = inventoryList.stream().filter(
                x -> DomUtil.exceedFourthExpired(x.getDom(), guaranteePeriod)).mapToLong(
                WarehouseInventory::getTotalQuantity).sum();
            thirdExpiredTotal = inventoryList.stream().filter(
                x -> DomUtil.exceedThirdExpired(x.getDom(), guaranteePeriod)).mapToLong(
                WarehouseInventory::getTotalQuantity).sum();
            approachExpiredTotal = inventoryList.stream().filter(
                x -> DomUtil.approachExpired(x.getDom(), guaranteePeriod)).mapToLong(
                WarehouseInventory::getTotalQuantity).sum();
            normalTotal = inventoryList.stream().filter(x -> DomUtil.lessFourthExpired(x.getDom(), guaranteePeriod))
                .mapToLong(WarehouseInventory::getTotalQuantity).sum();
        } else {
            detail.setSpecTag("1");
        }
        detail.setFourthExpiredQuantity(fourthExpiredQuantity);
        detail.setThirdExpiredQuantity(thirdExpiredQuantity);
        detail.setApproachExpiredQuantity(approachExpiredQuantity);
        detail.setNormalQuantity(normalQuantity);
        detail.setFourthExpiredTotal(fourthExpiredTotal);
        detail.setThirdExpiredTotal(thirdExpiredTotal);
        detail.setApproachExpiredTotal(approachExpiredTotal);
        detail.setNormalTotal(normalTotal);
        detail.setUsableQuantity(inventoryList.stream().mapToLong(WarehouseInventory::getAvailableQuantity).sum());
        return detail;
    }

    private WarehouseInventoryStats collectInventoryStats(List<WarehouseInventoryDetail> detailList) {
        WarehouseInventoryStats stats = new WarehouseInventoryStats();
        try {
            WarehouseInventoryDetail detail = detailList.get(0);
            stats.setWarehouseAreaId(detail.getWarehouseAreaId());
            stats.setWarehouseAreaName(detail.getWarehouseAreaName());
            stats.setZoneType(detail.getZoneType());
            stats.setBizUnitId(detail.getBizUnitId());
            stats.setSaleOrgId(detail.getSaleOrgId());
            stats.setStatisticalDate(DomUtil.getToday());
            stats.setTotalQuantity(detailList.stream().mapToLong(WarehouseInventoryDetail::getTotalQuantity).sum());
            stats.setAvailableQuantity(
                detailList.stream().mapToLong(WarehouseInventoryDetail::getAvailableQuantity).sum());
            stats.setFrozenQuantity(detailList.stream().mapToLong(WarehouseInventoryDetail::getFrozenQuantity).sum());
            stats.setWithholdingQuantity(
                detailList.stream().mapToLong(WarehouseInventoryDetail::getWithholdingQuantity).sum());
            stats.setOnwayQuantity(detailList.stream().mapToLong(WarehouseInventoryDetail::getOnwayQuantity).sum());
            stats.setInspectQuantity(detailList.stream().mapToLong(WarehouseInventoryDetail::getInspectQuantity).sum());
            stats.setFourthExpiredQuantity(
                detailList.stream().mapToLong(WarehouseInventoryDetail::getFourthExpiredQuantity).sum());
            stats.setThirdExpiredQuantity(
                detailList.stream().mapToLong(WarehouseInventoryDetail::getThirdExpiredQuantity).sum());
            stats.setApproachExpiredQuantity(
                detailList.stream().mapToLong(WarehouseInventoryDetail::getApproachExpiredQuantity).sum());
            stats.setNormalQuantity(detailList.stream().mapToLong(WarehouseInventoryDetail::getNormalQuantity).sum());
            stats.setExceedThreeMonthsQuantity(
                detailList.stream().mapToLong(WarehouseInventoryDetail::getExceedThreeMonthsQuantity).sum());
            stats.setEarlyTwoMonthsQuantity(
                detailList.stream().mapToLong(WarehouseInventoryDetail::getEarlyTwoMonthsQuantity).sum());
            stats.setMidTwoMonthsQuantity(
                detailList.stream().mapToLong(WarehouseInventoryDetail::getMidTwoMonthsQuantity).sum());
            stats.setLateTwoMonthsQuantity(
                detailList.stream().mapToLong(WarehouseInventoryDetail::getLateTwoMonthsQuantity).sum());
            stats.setEarlyOneMonthQuantity(
                detailList.stream().mapToLong(WarehouseInventoryDetail::getEarlyOneMonthQuantity).sum());
            stats.setMidOneMonthQuantity(
                detailList.stream().mapToLong(WarehouseInventoryDetail::getMidOneMonthQuantity).sum());
            stats.setLateOneMonthQuantity(
                detailList.stream().mapToLong(WarehouseInventoryDetail::getLateOneMonthQuantity).sum());
            stats.setEarlyThisMonthQuantity(
                detailList.stream().mapToLong(WarehouseInventoryDetail::getEarlyThisMonthQuantity).sum());
            stats.setMidThisMonthQuantity(
                detailList.stream().mapToLong(WarehouseInventoryDetail::getMidThisMonthQuantity).sum());
            stats.setLateThisMonthQuantity(
                detailList.stream().mapToLong(WarehouseInventoryDetail::getLateThisMonthQuantity).sum());
        } catch (Exception e) {
            log.info("collectInventoryStats======haserror:{}", e.toString());
            log.info("collectInventoryStats======detailist:{}", JSON.toJSON(detailList));
        }
        //Integer undeliveredQuantity = getUndeliveredQuantity(warehouseAreaId);
        //stats.setUndeliveredQuantity(Long.valueOf(undeliveredQuantity));
        return stats;
    }


    private void initZeroInventoryQuantity(WarehouseInventoryDetail detail, WarehouseInventoryStats stats) {
        if(Objects.nonNull(detail)){
            //detail.setFrozenQuantity(0L);
            detail.setTotalQuantity(0L);
            detail.setAvailableQuantity(0L);
            detail.setWithholdingQuantity(0L);
            detail.setOnwayQuantity(0L);
            detail.setInspectQuantity(0L);
            //detail.setUndeliveredQuantity(0L);
            detail.setFourthExpiredQuantity(0L);
            detail.setThirdExpiredQuantity(0L);
            detail.setApproachExpiredQuantity(0L);
            detail.setNormalQuantity(0L);
            detail.setExceedThreeMonthsQuantity(0L);
            detail.setEarlyTwoMonthsQuantity(0L);
            detail.setMidTwoMonthsQuantity(0L);
            detail.setLateTwoMonthsQuantity(0L);
            detail.setEarlyOneMonthQuantity(0L);
            detail.setMidOneMonthQuantity(0L);
            detail.setLateOneMonthQuantity(0L);
            detail.setEarlyThisMonthQuantity(0L);
            detail.setMidThisMonthQuantity(0L);
            detail.setLateThisMonthQuantity(0L);
            detail.setGmt_modified(new Date());
        }
        if(Objects.nonNull(stats)){
            //stats.setFrozenQuantity(0L);
            stats.setTotalQuantity(0L);
            stats.setAvailableQuantity(0L);
            stats.setWithholdingQuantity(0L);
            stats.setOnwayQuantity(0L);
            stats.setInspectQuantity(0L);
            //stats.setUndeliveredQuantity(0L);
            stats.setFourthExpiredQuantity(0L);
            stats.setThirdExpiredQuantity(0L);
            stats.setApproachExpiredQuantity(0L);
            stats.setNormalQuantity(0L);
            stats.setExceedThreeMonthsQuantity(0L);
            stats.setEarlyTwoMonthsQuantity(0L);
            stats.setMidTwoMonthsQuantity(0L);
            stats.setLateTwoMonthsQuantity(0L);
            stats.setEarlyOneMonthQuantity(0L);
            stats.setMidOneMonthQuantity(0L);
            stats.setLateOneMonthQuantity(0L);
            stats.setEarlyThisMonthQuantity(0L);
            stats.setMidThisMonthQuantity(0L);
            stats.setLateThisMonthQuantity(0L);
            stats.setGmt_modified(new Date());
        }
    }

    private Boolean isZeroInventoryQuantity(WarehouseInventoryDetail detail, WarehouseInventoryStats stats) {
        if(Objects.nonNull(detail)){
            return detail.getTotalQuantity().equals(0L)
                    && detail.getAvailableQuantity().equals(0L)
                    && detail.getWithholdingQuantity().equals(0L)
                    && detail.getOnwayQuantity().equals(0L)
                    && detail.getInspectQuantity().equals(0L)
                    && detail.getFourthExpiredQuantity().equals(0L)
                    && detail.getThirdExpiredQuantity().equals(0L)
                    && detail.getApproachExpiredQuantity().equals(0L)
                    && detail.getNormalQuantity().equals(0L)
                    && detail.getExceedThreeMonthsQuantity().equals(0L)
                    && detail.getEarlyTwoMonthsQuantity().equals(0L)
                    && detail.getMidTwoMonthsQuantity().equals(0L)
                    && detail.getLateTwoMonthsQuantity().equals(0L)
                    && detail.getEarlyOneMonthQuantity().equals(0L)
                    && detail.getMidOneMonthQuantity().equals(0L)
                    && detail.getLateOneMonthQuantity().equals(0L)
                    && detail.getEarlyThisMonthQuantity().equals(0L)
                    && detail.getMidThisMonthQuantity().equals(0L)
                    && detail.getLateThisMonthQuantity().equals(0L);
        }
        if(Objects.nonNull(stats)){
            return stats.getTotalQuantity().equals(0L)
                    && stats.getAvailableQuantity().equals(0L)
                    && stats.getWithholdingQuantity().equals(0L)
                    && stats.getOnwayQuantity().equals(0L)
                    && stats.getFourthExpiredQuantity().equals(0L)
                    && stats.getThirdExpiredQuantity().equals(0L)
                    && stats.getApproachExpiredQuantity().equals(0L)
                    && stats.getNormalQuantity().equals(0L)
                    && stats.getExceedThreeMonthsQuantity().equals(0L)
                    && stats.getEarlyTwoMonthsQuantity().equals(0L)
                    && stats.getMidTwoMonthsQuantity().equals(0L)
                    && stats.getLateTwoMonthsQuantity().equals(0L)
                    && stats.getEarlyOneMonthQuantity().equals(0L)
                    && stats.getMidOneMonthQuantity().equals(0L)
                    && stats.getLateOneMonthQuantity().equals(0L)
                    && stats.getEarlyThisMonthQuantity().equals(0L)
                    && stats.getMidThisMonthQuantity().equals(0L)
                    && stats.getLateThisMonthQuantity().equals(0L);
        }
        return false;
    }

    //取下单未走量
    private Integer getUndeliveredQuantity(String warehouseAreaId) {
        CountSaleOrderForReportRequest request = new CountSaleOrderForReportRequest();
        request.setWareHouseId(warehouseAreaId);
        /**
         * CANCELLED(-1, "已取消"),
         * BEFORE_SUBMIT(1, "待提交"),
         * BEFORE_DELIVERY(2, "待交货"),
         * PARTIAL_DELIVER(3, "部分交货"),
         * COMPLETED(4, "已完成"),
         * SHIPPED(5, "已发货"),
         * STAY_PUSH(6, "待推送"),
         * PUSHING(7, "已推送"),
         * PARTIAL_HANDLE(11, "部分处理"),
         * WAIT_HANDLE(12, "待处理"),
         * WAIT_RECEIVING(13, "待收货"),
         * PART_RECEIVING(14, "部分收货");
         */
        Integer[] status = {2, 3};
        Date now = new Date();
        request.setGmtCreateStartTime(DomUtil.getStartTimeOfMonth(now));
        request.setGmtCreateEndTime(now);
        request.setDetailStatusList(Arrays.asList(status));
        try {
            CountSaleOrderForReportResponse response = otsB2bTradeCenterService.countSaleOrderForReport(request);
            return response.getQuantity() - Integer.valueOf(response.getDeliveryAmount().split("\\.")[0]);
        } catch (Exception e) {
            log.error("获取下单未走量出错,cause={}", e);
            return 0;
        }
    }

    //取库存记录总数
    private int getInventoryTotal(InventoryTypeEnum typeEnum) {
        GetListWarehouseStockRequest request = new GetListWarehouseStockRequest();
        request.setPage(PAGE_NO_1);
        request.setSize(PAGE_SIZE_400);
        request.setType(String.valueOf(typeEnum.getType()));
        return otsUnifiedInventoryService.getListWarehouseStock(request).getTotal();
    }

    private void handleInventoryOccupy(String warehouseAreaId,List<WarehouseInventoryDetail> businessInventoryDTOList) {
        log.info("before_handleInventoryOccupy_businessInventoryDTOList={}",businessInventoryDTOList.size());
        Map<String, WarehouseInventoryDetail> inventorySDOMap = businessInventoryDTOList.stream().collect(
            Collectors.toMap(x -> getUniqueKey(x), Function.identity(), (a, b) -> a));

        //正向
        //订单行状态为待发货、部分发货，按照【确认数量-发货数量】计算统计发货仓库占用
        List<AllocateUnshippedQuantitySDO> aaa = queryAllocateUnshippedQuantity(warehouseAreaId,businessInventoryDTOList);
        aaa.stream().forEach(x -> {
            WarehouseInventoryDetail detail = inventorySDOMap.get(getUniqueKey(x));
            if (detail != null) {
                Long confirmQuantity = x.getConfirmQuantity() != null ? x.getConfirmQuantity() : 0L;
                Long shipQuantity = x.getShipQuantity() != null ? x.getShipQuantity() : 0L;
                Long unShippedQuantity = (confirmQuantity - shipQuantity) < 0 ? 0
                    : (confirmQuantity - shipQuantity) * 1000;
                detail.setUsableQuantity(detail.getUsableQuantity() - unShippedQuantity);
                detail.setWithholdingQuantity(detail.getWithholdingQuantity() + unShippedQuantity);
            }
        });

        //逆向
        //订单行状态为待发货、部分发货，按照【订单数量-发货数量】计算统计收货仓库占用
        List<AllocateUnshippedQuantitySDO> bbb = queryAllocateUnshippedQuantityReturn(warehouseAreaId,businessInventoryDTOList);
        bbb.stream().forEach(x -> {
            WarehouseInventoryDetail detail = inventorySDOMap.get(getUniqueKeyReturn(x));
            if (detail != null) {
                Long applyQuantity = x.getApplyQuantity() != null ? x.getApplyQuantity() : 0L;
                Long shipQuantity = x.getShipQuantity() != null ? x.getShipQuantity() : 0L;
                Long unShippedQuantity = (applyQuantity - shipQuantity) < 0 ? 0 : (applyQuantity - shipQuantity) * 1000;
                detail.setUsableQuantity(detail.getUsableQuantity() - unShippedQuantity);
                detail.setWithholdingQuantity(detail.getWithholdingQuantity() + unShippedQuantity);
            }
        });
        //log.info("after_handleInventoryOccupy_businessInventoryDTOList={}",JSON.toJSONString(businessInventoryDTOList));
    }

    private List<AllocateUnshippedQuantitySDO> queryAllocateUnshippedQuantity(String shipWarehouseAreaId,
        List<WarehouseInventoryDetail> businessInventoryDTOList) {
        int total = businessInventoryDTOList.size();
        int pages = total / PAGE_SIZE_400;
        if (total % PAGE_SIZE_400 > 0) {
            pages++;
        }
        List<Future<List<AllocateUnshippedQuantitySDO>>> futureList = Lists.newArrayList();
        for (int i = 0; i < pages; i++) {
            int finalI = i;
            int finalPages = pages;
            CompletableFuture<List<AllocateUnshippedQuantitySDO>> future = CompletableFuture.supplyAsync(() -> {
                List<WarehouseInventoryDetail> inventoryDTOList;
                if (finalI == finalPages - 1) {
                    inventoryDTOList = businessInventoryDTOList.subList(finalI * PAGE_SIZE_400, total);
                } else {
                    inventoryDTOList = businessInventoryDTOList.subList(finalI * PAGE_SIZE_400,
                        (finalI + 1) * PAGE_SIZE_400);
                }
                List<String> scItemIds = inventoryDTOList.stream().map(WarehouseInventoryDetail::getGoodsId).distinct()
                    .collect(Collectors.toList());
                List<AllocateUnshippedQuantitySDO> list = new ArrayList<>();
                GetAllocateUnshippedQuantityRequest requestNormal = new GetAllocateUnshippedQuantityRequest();
                requestNormal.setShipWarehouseAreaId(shipWarehouseAreaId);
                requestNormal.setScItemIds(scItemIds);
                requestNormal.setReturnType(0);
                try {
                    Result<List<AllocateUnshippedQuantitySDO>> allocateUnshippedQuantity = allocateOrderDetailService
                        .getAllocateUnshippedQuantity(requestNormal);
                    if (CollectionUtils.isEmpty(allocateUnshippedQuantity.getResult())) {
                        return list;
                    }
                    list = allocateUnshippedQuantity.getResult();
                    log.info("queryAllocateUnshippedQuantity_request={},result_size={}",
                        JSON.toJSONString(requestNormal), allocateUnshippedQuantity.getResult().size());
                } catch (Exception e) {
                    log.error("queryAllocateUnshippedQuantity_error", e);
                }
                return list;
            }, inventorySyncTaskExecutor);
            futureList.add(future);
        }
        List<AllocateUnshippedQuantitySDO> subList = new ArrayList<>();
        try {
            for (Future<List<AllocateUnshippedQuantitySDO>> future : futureList) {
                List<AllocateUnshippedQuantitySDO> result = future.get();
                subList.addAll(result);
            }
        } catch (Exception e) {
            log.error("获取多线程查询结果异常", e);
        }
        return subList;
    }

    private List<AllocateUnshippedQuantitySDO> queryAllocateUnshippedQuantityReturn(String receiveWarehouseAreaId,
        List<WarehouseInventoryDetail> businessInventoryDTOList) {
        int total = businessInventoryDTOList.size();
        int pages = total / PAGE_SIZE_400;
        if (total % PAGE_SIZE_400 > 0) {
            pages++;
        }
        List<Future<List<AllocateUnshippedQuantitySDO>>> futureList = Lists.newArrayList();
        for (int i = 0; i < pages; i++) {
            int finalI = i;
            int finalPages = pages;
            CompletableFuture<List<AllocateUnshippedQuantitySDO>> future = CompletableFuture.supplyAsync(() -> {
                List<WarehouseInventoryDetail> inventoryDTOList;
                if (finalI == finalPages - 1) {
                    inventoryDTOList = businessInventoryDTOList.subList(finalI * PAGE_SIZE_400, total);
                } else {
                    inventoryDTOList = businessInventoryDTOList.subList(finalI * PAGE_SIZE_400,
                        (finalI + 1) * PAGE_SIZE_400);
                }
                List<String> scItemIds = inventoryDTOList.stream().map(WarehouseInventoryDetail::getGoodsId).distinct()
                    .collect(Collectors.toList());
                List<AllocateUnshippedQuantitySDO> list = new ArrayList<>();
                GetAllocateUnshippedQuantityRequest requestNormal = new GetAllocateUnshippedQuantityRequest();
                requestNormal.setReceiveWarehouseAreaId(receiveWarehouseAreaId);
                requestNormal.setScItemIds(scItemIds);
                requestNormal.setReturnType(1);
                try {
                    Result<List<AllocateUnshippedQuantitySDO>> allocateUnshippedQuantity = allocateOrderDetailService
                        .getAllocateUnshippedQuantity(requestNormal);
                    if (CollectionUtils.isEmpty(allocateUnshippedQuantity.getResult())) {
                        return list;
                    }
                    list = allocateUnshippedQuantity.getResult();
                    log.info("queryAllocateUnshippedQuantityReturn_request={},result_size={}",
                        JSON.toJSONString(requestNormal), allocateUnshippedQuantity.getResult().size());
                } catch (Exception e) {
                    log.error("queryAllocateUnshippedQuantityReturn_error", e);
                }
                return list;
            }, inventorySyncTaskExecutor);
            futureList.add(future);
        }
        List<AllocateUnshippedQuantitySDO> subList = new ArrayList<>();
        try {
            for (Future<List<AllocateUnshippedQuantitySDO>> future : futureList) {
                List<AllocateUnshippedQuantitySDO> result = future.get();
                subList.addAll(result);
            }
        } catch (Exception e) {
            log.error("获取多线程查询结果异常", e);
        }
        return subList;
    }

    private String getUniqueKey(WarehouseInventoryDetail businessInventoryDTO) {
        return businessInventoryDTO.getWarehouseAreaId() + "_" + businessInventoryDTO.getGoodsId();
    }

    private String getUniqueKey(AllocateUnshippedQuantitySDO allocateUnshippedQuantitySDO) {
        return allocateUnshippedQuantitySDO.getShipAreaId() + "_" + allocateUnshippedQuantitySDO.getScItemId();
    }

    private String getUniqueKeyReturn(AllocateUnshippedQuantitySDO allocateUnshippedQuantitySDO) {
        return allocateUnshippedQuantitySDO.getReceiveAreaId() + "_" + allocateUnshippedQuantitySDO.getScItemId();
    }

    @Override
    public void reAnalysisReport(String statisDate) {
        LoadListRequest request = new LoadListRequest();
        request.setStatisticalDate(statisDate);
        Result<List<WarehouseInventoryDetail>> response;
        List<WarehouseInventoryDetail> waitReAnalysisList = new ArrayList<>();
        for (int i = 0; i < 2000; i++) {
            if(i==0){
                request.setStart(0);
            }else{
                request.setStart(i * 2000 + 1);
            }
            request.setLimit(2000);
            response = warehouseInventoryDetailService.loadList(request);
            if(CollectionUtils.isNotEmpty(response.getResult())){
                waitReAnalysisList.addAll(response.getResult());
            }else{
                break;
            }
        }
        Map<String, List<WarehouseInventoryDetail>> mapOrg = waitReAnalysisList.stream().collect(Collectors.groupingBy(x->x.getWarehouseAreaId()));
        List<WarehouseInventoryStats> statsList = new ArrayList<>();
        for (Map.Entry<String, List<WarehouseInventoryDetail>> entryOrg : mapOrg.entrySet()) {
            String warehouseAreaId = entryOrg.getKey();
            List<WarehouseInventoryDetail> orgInventoryList = entryOrg.getValue();
            if (CollectionUtils.isNotEmpty(orgInventoryList)) {
                WarehouseInventoryStats stats= collectInventoryStats(orgInventoryList);
                stats.setStatisticalDate(statisDate);
                statsList.add(stats);
            }
        }
        if (CollectionUtils.isNotEmpty(statsList)) {
            for (WarehouseInventoryStats todo : statsList) {
                //已存在的数据则更新
                QueryByWarehouseAreaIdsRequest request2 = new QueryByWarehouseAreaIdsRequest();
                request2.setWarehouseAreaIds(Arrays.asList(todo.getWarehouseAreaId()));
                request2.setStatisticalDate(statisDate);
                Result<List<WarehouseInventoryStats>> response2 = warehouseInventoryStatsService.queryByWarehouseAreaIds(request2);
                List<WarehouseInventoryStats> existedList = response2.getResult();
                WarehouseInventoryStats existed = existedOfList(todo, existedList);
                if (existed != null) {
                    if (!needModify(todo, existed)) {
                        log.info("WarehouseInventoryStats_record exist,warehouseid={}", todo.getWarehouseAreaId());
                        continue;
                    }
                    BeanUtils.copyProperties(todo, existed, "id");
                    existed.setGmt_modified(new Date());
                    Object updateResponse = warehouseInventoryStatsService.update(existed);
                } else {
                    Object createResponse = warehouseInventoryStatsService.create(todo);
                }
            }
        }
    }
}
